﻿Imports System.Text.RegularExpressions
Public Class HarryNode
    Public Shared pathSeparator As String = "."
    Public Shared outputFormat As Boolean = True
    Public Shared formatRetraction As Integer = 2
    Public Shared Function MulReplace(source As String, ParamArray args() As String) As String
        If args.Length Mod 2 <> 0 Then
            Return source
        End If
        For i As Integer = 0 To UBound(args) Step 2
            source = Replace(source, args(i), args(i + 1))
        Next
        Return source
    End Function
    Public Shared Function ToEscape(source As String) As String
        Return MulReplace(source, "\", "\\", vbLf, "\n", vbCr, "\r", vbCrLf, "\n", vbTab, "\t", """", "\""", Chr(8), "\b", Chr(12), "\f")
    End Function
    Public Enum NodeTypeEnum
        isNull = 0
        isString = 1
        isSingle = 2
        isDict = 3
        isList = 4
        isBool = 5
    End Enum
    Public nodeType As NodeTypeEnum
    Public nodeName As String
    Public parentNode As HarryNode

    Private stringValue As String
    Private singleValue As Decimal
    Private boolValue As Boolean
    Public childNode As Dictionary(Of String, HarryNode)

    Public Event NodeContentChangeBefore(ByRef path As String, ByRef newValue As Object, ByRef newValueType As String)
    Public Event NodeContentChangeBeforeFromJson(ByRef path As String, ByRef json As String)

    Public Event NodeContentChangeLater(path As String, ByRef nowValue As Object, ByRef newValueType As NodeTypeEnum)
    Public Event NodeContentChangeLaterFromJson(path As String, nowJson As String)
    Public Function ToStringWithStringListDict() As List(Of Dictionary(Of String, String))
        Dim result As New List(Of Dictionary(Of String, String))
        For Each node In childNode
            result.Add(node.Value.ToDict)
        Next
        Return result
    End Function
    Public Sub Merge(node As HarryNode)
        If nodeType = node.nodeType And nodeType = NodeTypeEnum.isDict Then
            For i = 0 To node.childNode.Count - 1
                Dim key = node.childNode.Keys(i)
                If childNode.ContainsKey(key) Then
                    childNode(key).Merge(node.childNode(key))
                Else
                    childNode.Add(key, node.childNode(key))
                End If
            Next
        End If
    End Sub
    Public Function GetChildPath() As List(Of String)
        Dim result As New List(Of String)
        If nodeType = NodeTypeEnum.isDict Or nodeType = NodeTypeEnum.isList Then
            result.AddRange(childNode.Keys)
        Else
            result.Add(nodeName)
        End If
        Return result
    End Function
    'Public Function GetTreeNode(interpreter As 解释器) As TreeNode
    '    Dim rootNode As New TreeNode(nodeName & interpreter.Search(nodeName))
    '    If nodeType = NodeTypeEnum.isDict Or nodeType = NodeTypeEnum.isList Then
    '        For Each cNode In childNode
    '            rootNode.Nodes.Add(cNode.Value.GetTreeNode(interpreter))
    '        Next
    '    Else
    '        rootNode.Nodes.Add(Value & interpreter.Search(Value))
    '    End If
    '    Return rootNode
    'End Function
    Public Sub Power(path As String, addValue As Single)
        SetAtt(path, GetAtt(path, 0) ^ addValue, 0)
    End Sub
    Public Sub Add(path As String, addValue As Single)
        SetAtt(path, GetAtt(path, 0) + addValue, 0)
    End Sub
    Public Sub ConAdd(path As String, addValue As Single, maxValue As Single, Optional minValue As Single = 0)
        Dim newValue As Single = GetAtt(path, 0) + addValue
        If newValue > maxValue Then
            newValue = maxValue
        End If
        If newValue < minValue Then
            newValue = minValue
        End If
        SetAtt(path, newValue, 0)
    End Sub
    Public Sub Mul(path As String, addValue As Single)
        SetAtt(path, GetAtt(path, 0) * addValue, 0)
    End Sub
    Public Sub AddNode(node As HarryNode)
        If nodeType <> NodeTypeEnum.isList Then
            Return
        End If
        Dim nodeName As String = Str(childNode.Count)
        childNode.Add(Str(childNode.Count), node)
    End Sub
    Public Sub AddNode(path As String, nodeName As String, nodeJson As String)
        Dim paths() As String = path.Split(pathSeparator）
        Dim p As String
        Dim node As HarryNode = Me
        For i As Integer = 0 To UBound(paths) - 1
            p = paths(i)
            Select Case node.nodeType
                Case NodeTypeEnum.isDict, NodeTypeEnum.isList
                    If node.nodeType = NodeTypeEnum.isList Then
                        p = Int(Val(p))
                    End If
                    If Not node.childNode.ContainsKey(p) Then
                        node.childNode.Add(p, New HarryNode(p, "{}", Me))
                    End If
                Case Else
                    node.nodeType = NodeTypeEnum.isDict
                    node.childNode = New Dictionary(Of String, HarryNode)
                    If Not node.childNode.ContainsKey(p) Then
                        node.childNode.Add(p, New HarryNode(p, "{}", Me))
                    End If
            End Select
            node = node.childNode(p)
        Next
        p = paths.Last
        Select Case node.nodeType
            Case NodeTypeEnum.isDict, NodeTypeEnum.isList
                If node.nodeType = NodeTypeEnum.isList Then
                    p = Int(Val(p))
                End If
                If Not node.childNode.ContainsKey(p) Then
                    node.childNode.Add(p, New HarryNode(p, "{}", Me))
                End If
            Case Else
                node.nodeType = NodeTypeEnum.isDict
                node.childNode = New Dictionary(Of String, HarryNode)
                If Not node.childNode.ContainsKey(p) Then
                    node.childNode.Add(p, New HarryNode(p, "{}", Me))
                End If
        End Select
        If Not node.childNode.ContainsKey(p) Then
            node.childNode.Add(p, New HarryNode(nodeName, nodeJson, Me))
        Else
            node.childNode(p) = New HarryNode(nodeName, nodeJson, Me)
        End If
    End Sub
    Public Sub RemoveLast()
        If nodeType <> NodeTypeEnum.isList Then
            Return
        End If
        childNode.Remove(childNode.Last.Key)
    End Sub
    Public Sub Del(path As String)
        Dim paths() As String = path.Split(pathSeparator）
        Dim p As String
        Dim node As HarryNode = Me
        For i As Integer = 0 To UBound(paths) - 1
            p = paths(i)
            Select Case node.nodeType
                Case NodeTypeEnum.isDict, NodeTypeEnum.isList
                    If node.nodeType = NodeTypeEnum.isList Then
                        p = Int(Val(p))
                    End If
                    If Not node.childNode.ContainsKey(p) Then
                        Return
                    End If
                Case Else
                    node.nodeType = NodeTypeEnum.isDict
                    node.childNode = New Dictionary(Of String, HarryNode)
                    If Not node.childNode.ContainsKey(p) Then
                        Return
                    End If
            End Select
            node = node.childNode(p)
        Next
        p = paths.Last
        Select Case node.nodeType
            Case NodeTypeEnum.isDict, NodeTypeEnum.isList
                If node.nodeType = NodeTypeEnum.isList Then
                    p = Int(Val(p))
                End If
                If Not node.childNode.ContainsKey(p) Then
                    Return
                End If
            Case Else
                node.nodeType = NodeTypeEnum.isDict
                node.childNode = New Dictionary(Of String, HarryNode)
                If Not node.childNode.ContainsKey(p) Then
                    Return
                End If
        End Select
        node.childNode.Remove(p)
    End Sub
    Public Function GetAtt(path As String, Optional defaultValue As Object = Nothing) As Object
        If path = "" Then
            Return Value
        End If
        Dim paths() As String = path.Split(pathSeparator）
        Dim node As HarryNode = Me
        For Each p As String In paths
            Select Case node.nodeType
                Case NodeTypeEnum.isDict, NodeTypeEnum.isList
                    If node.childNode.ContainsKey(p) Then
                        node = node.childNode(p)
                    Else
                        Return defaultValue
                    End If
                Case Else
                    Return defaultValue
            End Select
        Next
        Return node.Value
    End Function
    Public Function GetNode(path As String) As HarryNode
        If path = "" Then
            Return Me
        End If
        Dim p As String
        Dim paths() As String = path.Split(pathSeparator）
        Dim node As HarryNode = Me
        For i As Integer = 0 To UBound(paths) - 1
            p = paths(i)
            Select Case node.nodeType
                Case NodeTypeEnum.isDict, NodeTypeEnum.isList
                    If node.childNode.ContainsKey(p) Then
                        node = node.childNode(p)
                    Else
                        Return New HarryNode("", "", Me)
                    End If
                Case Else
                    Return New HarryNode("", "", Me)
            End Select
        Next
        If node.childNode IsNot Nothing AndAlso node.childNode.ContainsKey(paths.Last) Then
            Return node.childNode(paths.Last)
        End If
        Return New HarryNode(paths.Last, String.Format("""{0}""", paths.Last), Me)
    End Function
    Public Sub SetAtt(path As String, newValue As Object, newValueType As String)
        RaiseEvent NodeContentChangeBefore(path, newValue, newValueType)
        Dim paths() As String = path.Split(pathSeparator）
        Dim p As String
        Dim node As HarryNode = Me
        For i As Integer = 0 To UBound(paths) - 1
            p = paths(i)
            Select Case node.nodeType
                Case NodeTypeEnum.isDict, NodeTypeEnum.isList
                    If node.nodeType = NodeTypeEnum.isList Then
                        p = Int(Val(p))
                    End If
                    If Not node.childNode.ContainsKey(p) Then
                        node.childNode.Add(p, New HarryNode(p, "{}", Me))
                    End If
                Case Else
                    node.nodeType = NodeTypeEnum.isDict
                    node.childNode = New Dictionary(Of String, HarryNode)
                    If Not node.childNode.ContainsKey(p) Then
                        node.childNode.Add(p, New HarryNode(p, "{}", Me))
                    End If
            End Select
            node = node.childNode(p)
        Next
        p = paths.Last
        Select Case node.nodeType
            Case NodeTypeEnum.isDict, NodeTypeEnum.isList
                If node.nodeType = NodeTypeEnum.isList Then
                    p = Int(Val(p))
                End If
                If Not node.childNode.ContainsKey(p) Then
                    node.childNode.Add(p, New HarryNode(p, newValue, newValueType, Me))
                End If
            Case Else
                node.nodeType = NodeTypeEnum.isDict
                node.childNode = New Dictionary(Of String, HarryNode)
                If Not node.childNode.ContainsKey(p) Then
                    node.childNode.Add(p, New HarryNode(p, newValue, newValueType, Me))
                End If
        End Select
        node.childNode(p).Value = newValue
        RaiseEvent NodeContentChangeLater(path, node.childNode(p).Value, node.nodeType)
    End Sub
    Public Sub ReName(path As String, newName As String)
        Dim paths() As String = path.Split(pathSeparator）
        Dim p As String
        Dim node As HarryNode = Me
        For i As Integer = 0 To UBound(paths) - 1
            p = paths(i)
            Select Case node.nodeType
                Case NodeTypeEnum.isDict, NodeTypeEnum.isList
                    If node.nodeType = NodeTypeEnum.isList Then
                        p = Int(Val(p))
                    End If
                    If Not node.childNode.ContainsKey(p) Then
                        node.childNode.Add(p, New HarryNode(p, "{}", Me))
                    End If
                Case Else
                    node.nodeType = NodeTypeEnum.isDict
                    node.childNode = New Dictionary(Of String, HarryNode)
                    If Not node.childNode.ContainsKey(p) Then
                        node.childNode.Add(p, New HarryNode(p, "{}", Me))
                    End If
            End Select
            node = node.childNode(p)
        Next
        p = paths.Last
        Select Case node.nodeType
            Case NodeTypeEnum.isDict, NodeTypeEnum.isList
                If node.nodeType = NodeTypeEnum.isList Then
                    p = Int(Val(p))
                End If
                If node.childNode.ContainsKey(p) Then
                    ' 修改
                    node.childNode.Add(newName, New HarryNode(newName, node.childNode(p).ToJson, Me))
                    node.childNode.Remove(p)
                End If
            Case Else
                node.nodeType = NodeTypeEnum.isDict
                node.childNode = New Dictionary(Of String, HarryNode)
                If node.childNode.ContainsKey(p) Then
                    node.childNode.Add(newName, New HarryNode(newName, node.childNode(p).ToJson, Me))
                    node.childNode.Remove(p)
                End If
        End Select
    End Sub
    Public Sub SetAtt(path As String, json As String)
        RaiseEvent NodeContentChangeBeforeFromJson(path, json)
        Dim paths() As String = path.Split(pathSeparator）
        Dim p As String
        Dim node As HarryNode = Me
        For i As Integer = 0 To UBound(paths) - 1
            p = paths(i)
            Select Case node.nodeType
                Case NodeTypeEnum.isDict, NodeTypeEnum.isList
                    If node.nodeType = NodeTypeEnum.isList Then
                        p = Int(Val(p))
                    End If
                    If Not node.childNode.ContainsKey(p) Then
                        node.childNode.Add(p, New HarryNode(p, "{}", Me))
                    End If
                Case Else
                    node.nodeType = NodeTypeEnum.isDict
                    node.childNode = New Dictionary(Of String, HarryNode)
                    If Not node.childNode.ContainsKey(p) Then
                        node.childNode.Add(p, New HarryNode(p, "{}", Me))
                    End If
            End Select
            node = node.childNode(p)
        Next
        p = paths.Last
        Select Case node.nodeType
            Case NodeTypeEnum.isDict, NodeTypeEnum.isList
                If node.nodeType = NodeTypeEnum.isList Then
                    p = Int(Val(p))
                End If
                If Not node.childNode.ContainsKey(p) Then
                    node.childNode.Add(p, New HarryNode(p, "{}", Me))
                End If
            Case Else
                node.nodeType = NodeTypeEnum.isDict
                node.childNode = New Dictionary(Of String, HarryNode)
                If Not node.childNode.ContainsKey(p) Then
                    node.childNode.Add(p, New HarryNode(p, "{}", Me))
                End If
        End Select
        node.childNode(p).JsonToValue(json)
        RaiseEvent NodeContentChangeLaterFromJson(path, json)
    End Sub
    Public Function ToJson(Optional deep As Integer = 1, Optional indentChr As String = " ") As String
        If outputFormat Then
            Dim deepFormatRetraction = New String(indentChr, deep * formatRetraction)
            Dim deepFormatRetractionSub1 = New String(indentChr, (deep - 1) * formatRetraction)
            Select Case nodeType
                Case NodeTypeEnum.isString
                    Return String.Format("""{0}""", ToEscape(stringValue))
                Case NodeTypeEnum.isBool
                    Return boolValue.ToString.ToLower
                Case NodeTypeEnum.isSingle
                    Return singleValue
                Case NodeTypeEnum.isDict
                    Dim result As New List(Of String)
                    For i As Integer = 0 To childNode.Count - 1
                        result.Add(String.Format(deepFormatRetraction & """{0}"": {1}", childNode.Keys(i), childNode.Values(i).ToJson(deep + 1)))
                    Next
                    Return String.Format(Replace("{{\n{0}\n{1}}}", "\n", vbCrLf), Join(result.ToArray, "," & vbCrLf), deepFormatRetractionSub1)
                Case NodeTypeEnum.isList
                    Dim result As New List(Of String)
                    For i As Integer = 0 To childNode.Count - 1
                        result.Add(deepFormatRetraction & childNode.Values(i).ToJson(deep + 1))
                    Next
                    Return String.Format(Replace("[\n{0}\n{1}]", "\n", vbCrLf), Join(result.ToArray, "," & vbCrLf), deepFormatRetractionSub1)
                Case Else
                    Return ""
            End Select
        End If
        Select Case nodeType
            Case NodeTypeEnum.isString
                Return String.Format("""{0}""", ToEscape(stringValue))
            Case NodeTypeEnum.isBool
                Return boolValue
            Case NodeTypeEnum.isSingle
                Return singleValue
            Case NodeTypeEnum.isDict
                Dim result As New List(Of String)
                For i As Integer = 0 To childNode.Count - 1
                    result.Add(String.Format("""{0}"":{1}", childNode.Keys(i), childNode.Values(i).ToJson))
                Next
                Return String.Format("{{{0}}}", Join(result.ToArray, ","))
            Case NodeTypeEnum.isList
                Dim result As New List(Of String)
                For i As Integer = 0 To childNode.Count - 1
                    result.Add(childNode.Values(i).ToJson)
                Next
                Return String.Format("[{0}]", Join(result.ToArray, ","))
            Case Else
                Return ""
        End Select
    End Function
    Public Overloads Function ToString(Optional indentChr As String = " ") As String
        Return ToJson(indentChr:=indentChr)
    End Function
    Public Shared Widening Operator CType(ByVal d As HarryNode) As String
        Return d.ToString
    End Operator
    Public Shared Narrowing Operator CType(ByVal b As String) As HarryNode
        Return New HarryNode("", b, NodeTypeEnum.isString)
    End Operator
    Public Shared Widening Operator CType(ByVal d As HarryNode) As Dictionary(Of String, String)
        Dim r As New Dictionary(Of String, String)
        For Each kv In d.Value
            r.Add(kv.key, kv.value.value)
        Next
        Return r
    End Operator
    Public Shared Narrowing Operator CType(ByVal b As Dictionary(Of String, String)) As HarryNode
        Return New HarryNode("", b, NodeTypeEnum.isDict)
    End Operator
    Public Function ToDict() As Dictionary(Of String, String)
        Dim r As New Dictionary(Of String, String)
        For Each kv In Value
            r.Add(kv.key, kv.value.value)
        Next
        Return r
    End Function
    Public Property Value() As Object
        Get
            Select Case nodeType
                Case NodeTypeEnum.isString
                    Return stringValue
                Case NodeTypeEnum.isBool
                    Return boolValue
                Case NodeTypeEnum.isSingle
                    Return singleValue
                Case NodeTypeEnum.isDict
                    Return childNode
                Case NodeTypeEnum.isList
                    Return childNode.Values.ToList
                Case Else
                    Return Nothing
            End Select
        End Get
        Set(value As Object)
            Select Case nodeType
                Case NodeTypeEnum.isString
                    stringValue = value
                Case NodeTypeEnum.isBool
                    boolValue = value
                Case NodeTypeEnum.isSingle
                    singleValue = value
                Case NodeTypeEnum.isDict
                    childNode = value
                Case NodeTypeEnum.isList
                    Dim valueList As List(Of HarryNode) = value
                    childNode.Clear()
                    For i As Integer = 0 To valueList.Count - 1
                        childNode.Add(i, valueList(i))
                    Next
            End Select
        End Set
    End Property
    Public Sub JsonToValue(json As String)
        If json Is Nothing Then
            Return
        End If
        json = Regex.Match(json, "^\s*(.*?)\s*$", RegexOptions.Singleline).Groups(1).Value
        If Regex.IsMatch(json, "^"".*""$", RegexOptions.Singleline) Then
            '字符串
            nodeType = NodeTypeEnum.isString
            stringValue = json.Substring(1, json.Length - 2)
        ElseIf Regex.IsMatch(json, "^{.*}$", RegexOptions.Singleline) Then
            '字典
            nodeType = NodeTypeEnum.isDict
            If json = "{}" OrElse Regex.IsMatch(json, "^\s*\{\s*\}\s*$") Then
                childNode = New Dictionary(Of String, HarryNode)
            Else
                childNode = GetDict(json, Me)
            End If
        ElseIf Regex.IsMatch(json, "^\[.*\]$", RegexOptions.Singleline) Then
            '列表
            nodeType = NodeTypeEnum.isList
            If json = "[]" OrElse Regex.IsMatch(json, "^\s*\[\s*\]\s*$") Then
                childNode = New Dictionary(Of String, HarryNode)
            Else
                childNode = GetList(json, Me)
            End If
        ElseIf Regex.IsMatch(json, "^[-]{0,1}[\d]*[\.]{0,1}[\d]*$", RegexOptions.Singleline) Then
            '数值
            nodeType = NodeTypeEnum.isSingle
            singleValue = Convert.ToDecimal(json)
        Else
            '布尔值
            nodeType = NodeTypeEnum.isBool
            boolValue = GetBool(json)
        End If
    End Sub
    Public Shared Function GetDict(json As String, sourceNode As HarryNode) As Dictionary(Of String, HarryNode)
        'Debug.WriteLine(String.Format("GetDict.json={0}", json))
        Dim node As New Dictionary(Of String, HarryNode)
        Dim name As String = ""
        Dim temp As New List(Of String)
        Dim bigBrackets As Integer
        Dim colon As Integer
        Dim doubleQuotationMark As Integer
        Dim brackets As Integer
        Dim escape As Integer
        Dim onAscii As Boolean
        Dim asciiTemp As String = ""
        Dim stringContent As String
        Dim exegesis As Integer
        For Each c As String In json
            'Debug.WriteLine(Join(temp.ToArray, ""))
            'Debug.WriteLine("doubleQuotationMark={0}", doubleQuotationMark)
            'Debug.WriteLine("exegesis={0}", exegesis)
            'Debug.WriteLine("bigBrackets={0}", bigBrackets)
            'Debug.WriteLine("brackets={0}", brackets)
            'Debug.WriteLine("")
            If onAscii Then
                '在ascii转义中
                asciiTemp &= c
                If asciiTemp.Count > 3 Then
                    onAscii = False
                    Dim codePoint As Integer = Convert.ToInt32(asciiTemp, 16)
                    temp.Add(ChrW(codePoint))
                    asciiTemp = ""
                    onAscii = False
                    Continue For
                End If
            End If
            If doubleQuotationMark = 0 Then
                '未在字符串内时
                If c = "/" Then
                    exegesis += 1
                    Continue For
                ElseIf exegesis = 1 Then
                    temp.Add("/")
                    exegesis = 0
                End If
                If exegesis >= 2 Then
                    If c = vbCr Or c = vbLf Then
                        exegesis = 0
                    Else
                        Continue For
                    End If
                End If
                Select Case c
                    Case "{"
                        bigBrackets += 1
                        If bigBrackets > 1 OrElse brackets > 0 Then
                            '子嵌套记忆
                            temp.Add(c)
                        End If
                    Case "}"
                        bigBrackets -= 1
                        If bigBrackets > 1 OrElse brackets > 0 OrElse (bigBrackets = 1 AndAlso brackets = 0) Then
                            temp.Add(c)
                        End If
                    Case "["
                        brackets += 1
                        temp.Add(c)
                    Case "]"
                        brackets -= 1
                        temp.Add(c)
                    Case ":"
                        If bigBrackets = 1 AndAlso brackets = 0 Then
                            '第一层嵌套内
                            colon += 1
                        ElseIf bigBrackets > 1 OrElse brackets > 0 Then
                            temp.Add(c)
                        End If
                    Case """"
                        If bigBrackets = 1 AndAlso brackets = 0 Then
                            '第一层嵌套内
                            doubleQuotationMark += 1
                            temp.Add(c)
                        ElseIf bigBrackets > 1 OrElse brackets > 0 Then
                            temp.Add(c)
                        End If
                    Case ","
                        If colon > 0 AndAlso bigBrackets = 1 AndAlso brackets = 0 Then
                            '非字符串
                            If temp.Count > 0 Then
                                stringContent = Join(temp.ToArray, "")
                                temp.Clear()
                                node.Add(name, New HarryNode(name, stringContent, sourceNode))
                            Else
                                'null
                                node.Add(name, New HarryNode(name, Nothing, sourceNode))
                            End If
                            colon = 0
                        Else
                            temp.Add(c)
                        End If
                    Case Else
                        If bigBrackets > 1 Or Regex.IsMatch(c, "\S", RegexOptions.Singleline) Then
                            temp.Add(c)
                        End If
                End Select
            ElseIf bigBrackets = 1 AndAlso brackets = 0 Then
                '第一层嵌套内
                '在字符串内
                Select Case c
                    Case """"
                        temp.Add(c)
                        If escape = 1 Then
                            '转义"
                            escape = 0
                        Else
                            doubleQuotationMark = 0
                            If colon = 0 Then
                                '节点名
                                stringContent = Join(temp.ToArray, "")
                                temp.Clear()
                                name = stringContent.Substring(1, stringContent.Length - 2)
                            End If
                        End If
                    Case "u"
                        'ascii转义，收集后四位十六进制值
                        If escape = 1 Then
                            onAscii = True
                            escape = 0
                        Else
                            temp.Add(c)
                        End If
                    Case "\"
                        escape += 1
                        If escape > 1 Then
                            '转义\
                            temp.Add(c)
                            escape = 0
                        End If
                    Case "n"
                        If escape = 1 Then
                            '转义换行
                            temp.Add(vbLf)
                            escape = 0
                        Else
                            temp.Add(c)
                        End If
                    Case "r"
                        If escape = 1 Then
                            '转义换行
                            temp.Add(vbCr)
                            escape = 0
                        Else
                            temp.Add(c)
                        End If
                    Case "b"
                        If escape = 1 Then
                            temp.Add(Chr(8))
                            escape = 0
                        Else
                            temp.Add(c)
                        End If
                    Case "f"
                        If escape = 1 Then
                            temp.Add(Chr(12))
                            escape = 0
                        Else
                            temp.Add(c)
                        End If
                    Case "t"
                        If escape = 1 Then
                            temp.Add(vbTab)
                            escape = 0
                        Else
                            temp.Add(c)
                        End If
                    Case Else
                        escape = 0
                        temp.Add(c)
                End Select
            End If
        Next
        If temp.Count > 0 Then
            stringContent = Join(temp.ToArray, "")
            temp.Clear()
            node.Add(name, New HarryNode(name, stringContent, sourceNode))
        Else
            'null
            node.Add(name, New HarryNode(name, Nothing, sourceNode))
        End If
        Return node
    End Function
    Public Shared Function GetList(json As String, sourceNode As HarryNode) As Dictionary(Of String, HarryNode)
        'Debug.WriteLine(String.Format("GetList.json={0}", json))
        Dim node As New Dictionary(Of String, HarryNode)
        Dim name As String
        Dim temp As New List(Of String)
        Dim bigBrackets As Integer
        Dim doubleQuotationMark As Integer
        Dim brackets As Integer
        Dim escape As Integer
        Dim comma As Integer
        Dim stringContent As String
        Dim onAscii As Boolean
        Dim asciiTemp As String = ""
        For Each c As String In json
            If onAscii Then
                '在ascii转义中
                asciiTemp &= c
                If asciiTemp.Count > 3 Then
                    onAscii = False
                    Dim codePoint As Integer = Convert.ToInt32(asciiTemp, 16)
                    temp.Add(ChrW(codePoint))
                    asciiTemp = ""
                    onAscii = False
                    Continue For
                End If
            End If
            Dim exegesis As Integer
            If c = "/" Then
                exegesis += 1
                Continue For
            ElseIf exegesis = 1 Then
                temp.Add("/")
                exegesis = 0
            End If
            If exegesis >= 2 Then
                If c = vbCr Or c = vbLf Then
                    exegesis = 0
                Else
                    Continue For
                End If
            End If
            If doubleQuotationMark = 0 Then
                '未在字符串内时
                Select Case c
                    Case "["
                        brackets += 1
                        If brackets > 1 OrElse bigBrackets > 0 Then
                            '子嵌套记忆
                            temp.Add(c)
                        End If
                    Case "]"
                        brackets -= 1
                        If brackets > 1 OrElse bigBrackets > 0 OrElse (brackets = 1 AndAlso bigBrackets = 0) Then
                            temp.Add(c)
                        End If
                    Case "{"
                        bigBrackets += 1
                        temp.Add(c)
                    Case "}"
                        bigBrackets -= 1
                        temp.Add(c)
                    Case """"
                        If brackets = 1 AndAlso bigBrackets = 0 Then
                            '第一层嵌套内
                            doubleQuotationMark += 1
                            temp.Add(c)
                        ElseIf brackets > 1 OrElse bigBrackets > 0 Then
                            temp.Add(c)
                        End If
                    Case ","
                        If bigBrackets = 0 AndAlso brackets = 1 Then
                            name = comma
                            comma += 1
                            If temp.Count > 0 Then
                                stringContent = Join(temp.ToArray, "")
                                temp.Clear()
                                node.Add(name, New HarryNode(name, stringContent, sourceNode))
                            Else
                                'null
                                node.Add(name, New HarryNode(name, Nothing, sourceNode))
                            End If
                        Else
                            temp.Add(c)
                        End If
                    Case Else
                        If bigBrackets > 1 Or Regex.IsMatch(c, "\S", RegexOptions.Singleline) Then
                            temp.Add(c)
                        End If
                End Select
            ElseIf brackets = 1 AndAlso bigBrackets = 0 Then
                '第一层嵌套内
                '在字符串内
                Select Case c
                    Case """"
                        temp.Add(c)
                        If escape = 1 Then
                            '转义"
                            escape = 0
                        Else
                            doubleQuotationMark = 0
                        End If
                    Case "u"
                        'ascii转义，收集后四位十六进制值
                        If escape = 1 Then
                            onAscii = True
                            escape = 0
                        Else
                            temp.Add(c)
                        End If
                    Case "\"
                        escape += 1
                        If escape > 1 Then
                            '转义\
                            temp.Add(c)
                            escape = 0
                        End If
                    Case "n"
                        If escape = 1 Then
                            '转义换行
                            temp.Add(vbLf)
                            escape = 0
                        Else
                            temp.Add(c)
                        End If
                    Case "r"
                        If escape = 1 Then
                            '转义换行
                            temp.Add(vbCr)
                            escape = 0
                        Else
                            temp.Add(c)
                        End If
                    Case "b"
                        If escape = 1 Then
                            temp.Add(Chr(8))
                            escape = 0
                        Else
                            temp.Add(c)
                        End If
                    Case "f"
                        If escape = 1 Then
                            temp.Add(Chr(12))
                            escape = 0
                        Else
                            temp.Add(c)
                        End If
                    Case "t"
                        If escape = 1 Then
                            temp.Add(vbTab)
                            escape = 0
                        Else
                            temp.Add(c)
                        End If
                    Case Else
                        escape = 0
                        temp.Add(c)
                End Select
            End If
        Next
        name = comma
        If temp.Count > 0 Then
            '非字符串
            stringContent = Join(temp.ToArray, "")
            temp.Clear()
            node.Add(name, New HarryNode(name, stringContent, sourceNode))
        Else
            'null
            node.Add(name, New HarryNode(name, Nothing, sourceNode))
        End If
        Return node
    End Function
    Public Shared Function GetBool(value As String) As Boolean
        If value.ToLower = "false" OrElse value = "0" Then
            Return False
        End If
        Return True
    End Function
    Public Sub New(json As String)
        nodeName = ""
        parentNode = Nothing
        JsonToValue(json)
    End Sub
    Public Sub New(name As String, json As String, Optional parent As HarryNode = Nothing)
        nodeName = name
        parentNode = parent
        JsonToValue(json)
    End Sub
    Public Sub New(name As String, nodeValue As Object, type As NodeTypeEnum, Optional parent As HarryNode = Nothing)
        nodeName = name
        nodeType = type
        parentNode = parent
        Value = nodeValue
    End Sub
End Class